Bash 代表命令
前注
此笔记只用于回忆,不是教程。
原教程:阮一峰 Bash 脚本教程
代表命令
#!/bin/sh
# 或者
#! /bin/bash
echo -n hello world
echo -e "Hello\nWorld"
mkdir foo || mkdir bar
mkdir foo && mkdir bar
type echo
set
unset
?
~
*
ls ?[!a]?
mkdir {2007..2009}-{01..12}
echo {0..8..2}
echo $(date)
# 请注意,此命令默认关闭
ls **/*.txt
echo "$(cal)"
<< token
text
token
cat <<< 'hi there'
# 等同于
echo 'hi there' | cat
$?
$$
$_
$!
$0
$-
$#
$#
${#varname}
${varname:offset:length}
# 如果 pattern 匹配变量 variable 的开头,
# 删除最短匹配(非贪婪匹配)的部分,返回剩余部分
${variable#pattern}
# 如果 pattern 匹配变量 variable 的开头,
# 删除最长匹配(贪婪匹配)的部分,返回剩余部分
${variable##pattern}
# 如果 pattern 匹配变量 variable 的结尾,
# 删除最短匹配(非贪婪匹配)的部分,返回剩余部分
${variable%pattern}
# 如果 pattern 匹配变量 variable 的结尾,
# 删除最长匹配(贪婪匹配)的部分,返回剩余部分
${variable%%pattern}
# 如果 pattern 匹配变量 variable 的一部分,
# 最长匹配(贪婪匹配)的那部分被 string 替换,但仅替换第一个匹配
${variable/pattern/string}
# 如果 pattern 匹配变量 variable 的一部分,
# 最长匹配(贪婪匹配)的那部分被 string 替换,所有匹配都替换
${variable//pattern/string}
# 转为大写
${varname^^}
# 转为小写
${varname,,}
(( 2 + 2 ))
expr 3 + 2
!echo
export HISTTIMEFORMAT='%F %T '
export HISTIGNORE='pwd:ls:exit'
cd -
pushd dirname
popd [-n]
dirs
-c:清空目录栈。
-l:用户主目录不显示波浪号前缀,而打印完整的目录。
-p:每行一个条目打印目录栈,默认是打印在一行。
-v:每行一个条目,每个条目之前显示位置编号(从0开始)。
+N:N为整数,表示显示堆顶算起的第 N 个目录,从零开始。
-N:N为整数,表示显示堆底算起的第 N 个目录,从零开始。
grep -- "--hello" example.txt
# 写法一
if test -e /tmp/foo.txt ; then
echo "Found foo.txt"
fi
# 写法二
if [ -e /tmp/foo.txt ] ; then
echo "Found foo.txt"
fi
# 写法三
if [[ -e /tmp/foo.txt ]] ; then
echo "Found foo.txt"
fi
case expression in
pattern )
commands ;;
pattern )
commands ;;
esac
while condition; do
commands
done
for variable in list; do
commands
done
for (( i=0; i<5; i=i+1 )); do
echo $i
done
select name
[in list]
do
commands
done
# 函数总是在当前 Shell 执行,这是跟脚本的一个重大区别,Bash 会新建一个子 Shell 执行脚本。如果函数与脚本同名,函数会优先执行。但是,函数的优先级不如别名,即如果函数与别名同名,那么别名优先执行。
fn () {
foo=1
echo "fn: foo = $foo"
}
fn () {
local foo
foo=1
echo "fn: foo = $foo"
}
echo ${foo[@]}
# @和*放不放在双引号之中,是有差别的。
for i in "${names[@]}"; do
echo $i
done
echo ${#a[100]}
echo ${#a[@]}
# 或者
echo ${#a[*]}
# ${!array[@]}或${!array[*]},可以返回数组的成员序号,即哪些位置是有值的。
echo ${food[@]:0}
foo+=(d e f)
unset foo[2]
# unset ArrayName可以清空整个数组。
# 写法一
command || { echo "command failed"; exit 1; }
# 使用command || true,使得该命令即使执行失败,脚本也不会终止执行。
command || true
shopt
# 为了保证脚本退出时临时文件被删除,可以使用trap命令指定退出时的清除操作。
trap 'rm -f "$TMPFILE"' EXIT
TMPFILE=$(mktemp) || exit 1
echo "Our temp file is $TMPFILE"
快捷键
Ctrl + L:清除屏幕并将当前行移到页面顶部。Ctrl + C:中止当前正在执行的命令。Shift + PageUp:向上滚动。Shift + PageDown:向下滚动。Ctrl + U:从光标位置删除到行首。Ctrl + K:从光标位置删除到行尾。Ctrl + W:删除光标位置前一个单词。Ctrl + D:关闭 Shell 会话。↑,↓:浏览已执行命令的历史记录。Ctrl + o:执行历史文件里面的当前条目,并自动显示下一条命令。这对重复执行某个序列的命令很有帮助。!!:执行上一个命令。!n:n为数字,执行历史文件里面行号为n的命令。!-n:执行当前命令之前n条的命令。!string:执行最近一个以指定字符串string开头的命令。!?string:执行最近一条包含字符串string的命令。!$:代表上一个命令的最后一个参数。!*:代表上一个命令的所有参数,即除了命令以外的所有部分。^string1^string2:执行最近一条包含string1的命令,将其替换成string2。Alt + .:插入上一个命令的最后一个词。- 上面的
Alt + .快捷键,对于很长的文件路径,有时会非常方便。因为 Unix 命令的最后一个参数通常是文件路径。
- 上面的
其他
字符类
[[:alnum:]]:匹配任意英文字母与数字[[:alpha:]]:匹配任意英文字母[[:blank:]]:空格和 Tab 键。[[:cntrl:]]:ASCII 码 0-31 的不可打印字符。[[:digit:]]:匹配任意数字 0-9。[[:graph:]]:A-Z、a-z、0-9 和标点符号。[[:lower:]]:匹配任意小写字母 a-z。[[:print:]]:ASCII 码 32-127 的可打印字符。[[:punct:]]:标点符号(除了 A-Z、a-z、0-9 的可打印字符)。[[:space:]]:空格、Tab、LF(10)、VT(11)、FF(12)、CR(13)。[[:upper:]]:匹配任意大写字母 A-Z。[[:xdigit:]]:16进制字符(A-F、a-f、0-9)。
量词语法
?(pattern-list):模式匹配零次或一次。*(pattern-list):模式匹配零次或多次。+(pattern-list):模式匹配一次或多次。@(pattern-list):只匹配一次模式。!(pattern-list):匹配给定模式以外的任何内容。
常见环境变量
BASHPID:Bash 进程的进程 ID。BASHOPTS:当前 Shell 的参数,可以用shopt命令修改。DISPLAY:图形环境的显示器名字,通常是:0,表示 X Server 的第一个显示器。EDITOR:默认的文本编辑器。HOME:用户的主目录。HOST:当前主机的名称。IFS:词与词之间的分隔符,默认为空格。LANG:字符集以及语言编码,比如zh_CN.UTF-8。PATH:由冒号分开的目录列表,当输入可执行程序名后,会搜索这个目录列表。PS1:Shell 提示符。PS2: 输入多行命令时,次要的 Shell 提示符。PWD:当前工作目录。RANDOM:返回一个0到32767之间的随机数。SHELL:Shell 的名字。SHELLOPTS:启动当前 Shell 的set命令的参数,参见《set 命令》一章。TERM:终端类型名,即终端仿真器所用的协议。UID:当前用户的 ID 编号。USER:当前用户的用户名。
进制
number:没有任何特殊表示法的数字是十进制数(以10为底)。0number:八进制数。0xnumber:十六进制数。base#number:base进制的数。
文件判断
[ -a file ]:如果 file 存在,则为true。[ -b file ]:如果 file 存在并且是一个块(设备)文件,则为true。[ -c file ]:如果 file 存在并且是一个字符(设备)文件,则为true。[ -d file ]:如果 file 存在并且是一个目录,则为true。[ -e file ]:如果 file 存在,则为true。[ -f file ]:如果 file 存在并且是一个普通文件,则为true。[ -g file ]:如果 file 存在并且设置了组 ID,则为true。[ -G file ]:如果 file 存在并且属于有效的组 ID,则为true。[ -h file ]:如果 file 存在并且是符号链接,则为true。[ -k file ]:如果 file 存在并且设置了它的“sticky bit”,则为true。[ -L file ]:如果 file 存在并且是一个符号链接,则为true。[ -N file ]:如果 file 存在并且自上次读取后已被修改,则为true。[ -O file ]:如果 file 存在并且属于有效的用户 ID,则为true。[ -p file ]:如果 file 存在并且是一个命名管道,则为true。[ -r file ]:如果 file 存在并且可读(当前用户有可读权限),则为true。[ -s file ]:如果 file 存在且其长度大于零,则为true。[ -S file ]:如果 file 存在且是一个网络 socket,则为true。[ -t fd ]:如果 fd 是一个文件描述符,并且重定向到终端,则为true。 这可以用来判断是否重定向了标准输入/输出/错误。[ -u file ]:如果 file 存在并且设置了 setuid 位,则为true。[ -w file ]:如果 file 存在并且可写(当前用户拥有可写权限),则为true。[ -x file ]:如果 file 存在并且可执行(有效用户有执行/搜索权限),则为true。[ file1 -nt file2 ]:如果 FILE1 比 FILE2 的更新时间最近,或者 FILE1 存在而 FILE2 不存在,则为true。[ file1 -ot file2 ]:如果 FILE1 比 FILE2 的更新时间更旧,或者 FILE2 存在而 FILE1 不存在,则为true。[ FILE1 -ef FILE2 ]:如果 FILE1 和 FILE2 引用相同的设备和 inode 编号,则为true。
字符串判断
[ string ]:如果string不为空(长度大于0),则判断为真。[ -n string ]:如果字符串string的长度大于零,则判断为真。[ -z string ]:如果字符串string的长度为零,则判断为真。[ string1 = string2 ]:如果string1和string2相同,则判断为真。[ string1 == string2 ]等同于[ string1 = string2 ]。[ string1 != string2 ]:如果string1和string2不相同,则判断为真。[ string1 '>' string2 ]:如果按照字典顺序string1排列在string2之后,则判断为真。[ string1 '<' string2 ]:如果按照字典顺序string1排列在string2之前,则判断为真。
整数判断
[ integer1 -eq integer2 ]:如果integer1等于integer2,则为true。[ integer1 -ne integer2 ]:如果integer1不等于integer2,则为true。[ integer1 -le integer2 ]:如果integer1小于或等于integer2,则为true。[ integer1 -lt integer2 ]:如果integer1小于integer2,则为true。[ integer1 -ge integer2 ]:如果integer1大于或等于integer2,则为true。[ integer1 -gt integer2 ]:如果integer1大于integer2,则为true。
参数变量
$1~$9:函数的第一个到第9个的参数。$0:函数所在的脚本名。$#:函数的参数总数。$@:函数的全部参数,参数之间使用空格分隔。$*:函数的全部参数,参数之间使用变量$IFS值的第一个字符分隔,默认为空格,但是可以自定义。
set 命令
set -u遇到不存在的变量就会报错,并停止执行。set -x用来在运行结果之前,先输出执行的那一行命令。set -e使得脚本只要发生错误,就终止执行。set -o pipefail只要一个子命令失败,整个管道命令就失败,脚本就会终止执行。set -n:等同于set -o noexec,不运行命令,只检查语法是否正确。set -f:等同于set -o noglob,表示不对通配符进行文件名扩展。set -v:等同于set -o verbose,表示打印 Shell 接收到的每一行输入。set -o noclobber:防止使用重定向运算符>覆盖已经存在的文件。set -E参数可以纠正这个行为,使得函数也能继承trap命令。set -Eeuxo pipefail建议放在所有 Bash 脚本的头部。
环境变量
- 变量
LINENO返回它在脚本里面的行号。
登录 session
登录 Session 是用户登录系统以后,系统为用户开启的原始 Session,通常需要用户输入用户名和密码进行登录。
登录 Session 一般进行整个系统环境的初始化,启动的初始化脚本依次如下。
/etc/profile:所有用户的全局配置脚本。/etc/profile.d目录里面所有.sh文件~/.bash_profile:用户的个人配置脚本。如果该脚本存在,则执行完就不再往下执行。~/.bash_login:如果~/.bash_profile没找到,则尝试执行这个脚本(C shell 的初始化脚本)。如果该脚本存在,则执行完就不再往下执行。~/.profile:如果~/.bash_profile和~/.bash_login都没找到,则尝试读取这个脚本(Bourne shell 和 Korn shell 的初始化脚本)。
Linux 发行版更新的时候,会更新/etc里面的文件,比如/etc/profile,因此不要直接修改这个文件。如果想修改所有用户的登陆环境,就在/etc/profile.d目录里面新建.sh脚本。
非登录 session
非登录 Session 是用户进入系统以后,手动新建的 Session,这时不会进行环境初始化。比如,在命令行执行bash命令,就会新建一个非登录 Session。
非登录 Session 的初始化脚本依次如下。
/etc/bash.bashrc:对全体用户有效。~/.bashrc:仅对当前用户有效。
对用户来说,~/.bashrc通常是最重要的脚本。非登录 Session 默认会执行它,而登录 Session 一般也会通过调用执行它。每次新建一个 Bash 窗口,就相当于新建一个非登录 Session,所以~/.bashrc每次都会执行。注意,执行脚本相当于新建一个非互动的 Bash 环境,但是这种情况不会调用~/.bashrc。
转义字符
\a:响铃,计算机发出一记声音。\d:以星期、月、日格式表示当前日期,例如“Mon May 26”。\h:本机的主机名。\H:完整的主机名。\j:运行在当前 Shell 会话的工作数。\l:当前终端设备名。\n:一个换行符。\r:一个回车符。\s:Shell 的名称。\t:24小时制的hours:minutes:seconds格式表示当前时间。\T:12小时制的当前时间。\@:12小时制的AM/PM格式表示当前时间。\A:24小时制的hours:minutes表示当前时间。\u:当前用户名。\v:Shell 的版本号。\V:Shell 的版本号和发布号。\w:当前的工作路径。\W:当前目录名。\!:当前命令在命令历史中的编号。\#:当前 shell 会话中的命令数。\$:普通用户显示为$字符,根用户显示为#字符。\[:非打印字符序列的开始标志。\]:非打印字符序列的结束标志。